#所有论文函数调用接口
import random

import cv2
import numpy as np

# 判断形状
def shapePicture(a,b):
    # a是需要更改尺寸的图像
    #通过对比两个图像大小是否相等来裁剪
    (m, n) = np.shape(a)
    (m1, n1) = np.shape(b)
    if m !=m1:
        a = np.delete(a, -1, axis=0)  # =0行 =1列
    if n !=n1:
        a = np.delete(a, -1, axis=1)
    return a

# 显示图像
def showPicture(str1, picture):
    cv2.namedWindow(str(str1), 0)
    cv2.resizeWindow(str(str1), 1600, 900)
    cv2.imshow(str(str1), picture)

# 高斯金字塔
def gaussian(ori_image, down_times=3):
    # 1：添加第一个图像为原始图像
    temp_gau = ori_image.copy()
    for i in range(down_times):
        # 2：连续存储3次下采样，这样高斯金字塔就有3层
        temp_gau = cv2.pyrDown(temp_gau)
        showPicture(i+1, temp_gau)
    cv2.waitKey(0)
    return "success"

# 论文中拉普拉斯金字塔，是用原来图像减去向上采样图像，找到去掉的值
def laplian(ori_image, down_times=5):
    # 1：添加第一个图像为原始图像
    temp_gau = ori_image.copy()
    laplian_pic=[]
    for i in range(down_times):
        temp_gau1 = cv2.pyrDown(temp_gau) #向下采样
        temp_pyrup=cv2.pyrUp(temp_gau1)   #向上采样
        temp_pyrup=shapePicture(temp_pyrup,temp_gau)#判断两个图像是否一样大
        temp_lap = temp_gau-temp_pyrup
        laplian_pic.append(temp_lap)
        temp_gau = temp_gau1  # 迭代
        showPicture(i, temp_lap)  # 显示图像
    return laplian_pic #返回拉普拉斯金字塔操作后的图像

# 拉普拉斯金字塔（添加log算子的）
def laplianOfGauss(ori_image, down_times=5):

    # 1：添加第一个图像为原始图像
    temp_gau = ori_image.copy()
    laplian_pic=[]
    for i in range(down_times):
        # 2：连续存储5次下采样，这样高斯金字塔就有6层
        temp_gau1 = cv2.pyrDown(temp_gau) #向下采样
        temp_pyrup = cv2.pyrUp(temp_gau1)  # 向上采样
        temp_pyrup = shapePicture(temp_pyrup, temp_gau)  # 判断两个图像是否一样大
        temp_lap = temp_gau - temp_pyrup
        gaussPic = cv2.GaussianBlur(temp_lap, (3, 3), 0)
        logPic = cv2.Laplacian(gaussPic, -1, 3)#拉普拉斯边缘检测
        temp_gau = temp_gau1 #迭代
        laplian_pic.append(logPic)
        showPicture(i, logPic)  # 显示图像
    return laplian_pic #返回拉普拉斯金字塔操作后的图像

#log算子只有有明显边缘存在的时候才能捕捉到，算子计算图像
def logS(orinPic, time=3):
    showPicture("orin", orinPic)
    pic = orinPic.copy()
    downPic = cv2.pyrDown(pic)
    logPic = []
    for i in range(time):
        # log算子
        gaussPic = cv2.GaussianBlur(downPic, (3, 3), 0)  # 高斯模糊
        logs = cv2.Laplacian(gaussPic, -1, 3)  # 拉普拉斯算子求边缘锐化细节
        # showPicture(i,logs)            #显示图片
        logPic.append(logs)  # 保存信息返回
        downPic = cv2.pyrDown(downPic)  # 嵌套并向下采样

    return logPic

#log展开再整合
def logSS(orinPic, time):
    showPicture("orin", orinPic)
    pic = orinPic.copy()
    downPic = cv2.pyrDown(pic)
    logPic = []
    for i in range(time):
        # log算子
        gaussPic = cv2.GaussianBlur(downPic, (3, 3), 0)  # 高斯模糊
        logs = cv2.Laplacian(gaussPic, -1, 3)  # 拉普拉斯算子求边缘锐化细节
        # showPicture(i,logs)            #显示图片
        logPic.append(logs)  # 保存信息返回
        downPic = cv2.pyrDown(downPic)  # 嵌套

    # 三次up的效果
    downPic1 = cv2.pyrUp(downPic)  # 向上采样
    downPic1 = cv2.pyrUp(downPic1)  # 向上采样
    downPic1 = cv2.pyrUp(downPic1)  # 向上采样
    showPicture("upupup", downPic1)

    # 将上采样图像加上细节部分得出整理后的图像
    downPic = cv2.pyrUp(downPic)  # 向上采样
    # showPicture(time-1, downPic)
    downPic = shapePicture(downPic, logPic[time - 1])
    downPic = downPic + logPic[time - 1]
    # showPicture(time-1+10, downPic)
    for i in range(time - 1 - 1, -1, -1):
        downPic = cv2.pyrUp(downPic)  # 向上采样
        # showPicture(i, downPic)
        downPic = shapePicture(downPic, logPic[i])
        downPic = downPic + logPic[i]
        # showPicture(i+10, downPic)
    showPicture("last", downPic)
    return logPic

#reinhard色彩传递算法
def reinhard (target_rgb,reference_rgb):  #两个参数一个是目标图像一个是参考图像，返回的值是传递后的图像
    #将目标图像和参考图像转换为lab图像
    target_lab = cv2.cvtColor(target_rgb,cv2.COLOR_BGR2LAB)
    reference_lab = cv2.cvtColor(reference_rgb,cv2.COLOR_BGR2LAB)
    (mean_t, stddv_t) = cv2.meanStdDev(target_lab)     #计算目标图像的均值和方差
    (mean_r, stddv_r) = cv2.meanStdDev(reference_lab)  #计算参考图像的均值和方差
    #算法过程：目标图片的每一个像素值，减去目标图像均值然后乘上参考图片和目标图片标准差的比值，再加上参考图像均值
    #（目-目均）*参标/目标 +参均
    height,width,channel = target_lab.shape
    for i in range(0,height):
        for j in range(0,width):
            for k in range(0,channel):
                t = target_lab[i,j,k]
                t = (t-mean_t[k])*(stddv_r[k]/stddv_t[k]) + mean_r[k]
                t = 0 if t<0 else t
                t = 255 if t>255 else t
                target_lab[i,j,k] = t
    return(cv2.cvtColor(target_lab,cv2.COLOR_LAB2BGR))#再转回RGB图像

# 模糊隶属度计算函数
#计算了 max(bright,dark ) 一阶模糊距 阈值
#论文里的算法 算西格玛有点太麻烦了，而且没啥效果
import cv2
def fuzzySets(pic):
    # pic = cv2.cvtColor(pic,cv2.COLOR_BGR2GRAY) #将bgr图像转换为灰度图像
    height, width= pic.shape
    # 算出灰度值频率
    hist = cv2.calcHist([pic], [0], None, [255], [0, 255])
    # 求灰度值概率
    p = []
    s=flag=0
    Xcount=height*width
    for i in range(255):
        p.append(hist[i] / Xcount)
        s=s+p[i]
        if s<=0.5:
            flag=i/256  #这是这个的作用是找图像中像素点个数的中值给a
        if(flag==0):
            flag=0.1;
    # 一阶模糊矩
    m = 0
    for i in range(255):
        m += (i / 255) * p[i];
    #利用公式将图片数值分为 模糊矩阵dark与bright
    u_dark, u_bright = [], []
    a = flag                    #a是一个交叉点（这里设置为变量）
    l = (1 - 2 * a) / (a * a)       #拉姆达公式
    for i in range(0, height):
        for j in range(0, width):
            pic_now = pic[i][j]/255.00
            if pic_now <= a:
                u_dark.append((1 / a * (pic_now ** 2.00)))
            else:
                u_bright.append(((1 - (1 / a * (((1 - pic_now) / (1 + l * pic_now)) ** 2))) / (1 + l * (1 / a * (((1 - pic_now) / (1 + l * pic_now)) ** 2)))) )
    u_bright_max, u_dark_max = [max(u_bright)], [max(u_dark)] #求出他俩的最大值
    #做一个判断如果一阶模糊距大于零点五，那么bright_max 除2 就为 西格玛，反之一样
    if(m<=0.5):
        xgm=u_bright_max[0]/1.7
    else:
        xgm=u_dark_max[0]/2
    #返回阈值西格玛，为后续程序做铺垫
    return(xgm)

def gasuss_noise(image,mean=0,var=0.001):
    '''
    手动添加高斯噪声
    mean : 均值
    var : 方差
    '''
    image = np.array(image/255, dtype=float)
    noise = np.random.normal(mean, var ** 0.5, image.shape)  # 正态分布
    out = image + noise
    if out.min() < 0:
        low_clip = -1.
    else:
        low_clip = 0.
    out = np.clip(out, low_clip, 1.0)
    out = np.uint8(out*255)
    return out

def sp_noise(image,prob):
    '''
    手动添加椒盐噪声
    prob:噪声比例
    '''
    output = np.zeros(image.shape, np.uint8)
    thres = 1 - prob
    for i in range(image.shape[0]):
        for j in range(image.shape[1]):
            rdn = random.random()
            if rdn < prob:
                output[i][j] = 0
            elif rdn > thres:
                output[i][j] = 255
            else:
                output[i][j] = image[i][j]
    return output
